/**
 * 生成一个Monad Promise 数据,monad 就像一颗子弹，而生成moand的函数则负责对monad进行上膛,开动扳机。
 * Monad 就像一个炸弹，f 式把多个炸弹串联的方式。需要一个外界事件，如 detonate 就可以把monad个引爆了 
 * @param {*} e 状态数据，装入炸弹的火药。
 */
const Monad = e => { 

	const m = { // 一定要写成let m  确保m 的作用阈限定于  lambda 表达式范围，否则就会 定义成全局，后果不堪设想
		bind:f=>{m.ff.push(f);return m;},// 绑定一个函数到函数集合，这就像穿衣服一样，把m一层层的包裹起来
		ff:[],// 绑定函数的记录器,挂载到 m上的函数集合。第一元素最先调用，
		stateData:()=>e //monad 中的状态数据值,对于异步函数，这个状态数据无效，他是在异步响应结果里获取结果状态数据的。
	}// m

	return m;
};
module.exports.Monad = Monad;

/**
 * 引爆m0的回调函数,开启链式反应,这有点像 Array的reduce
 * @param {*} r 引爆m0说使用的引物
 * @param {*} m0  待引爆的的monad
 */
const detonate = r => m0 => {
	if(!m0 || !m0.ff || m0.ff.length<=0) {
		console.log(`monad 无效,m0:${m0},r:${r}`);
		return;
	}

	const f = m0.ff[0];// 提取第一个绑定函数
	const rff = m0.ff.slice(1);// 提取剩余的绑定函数,ff 代表f的数组.rff 表示right ff 或是 resting ff,数组右侧部分的意思。
	const isfunc = fn => Object.prototype.toString.call(fn)=== '[object Function]';// 判断fn是否是一个函数对象

	if(f && isfunc(f)){// 仅当f是函数的时候给予 进行函数回调
		// 创建新的绑定函数,这里为绑定函数传递参数。非常重要，是理解Monad实现的关键。用过回调f来触发下一个Monda而不是由bind直接触发。
		// 需要注意的是 f 触发的是 m0 即对m0中的参数进行回调，而不是对m1,m1的回调触发需要绑定在m1上的函数来给触发. 第一次回调
		const primer = !r ? m0.stateData() : r;  // 制作引物
		const m1 = f(primer);// m1,表示m的升级版的意思.,注意 这里忽略m 的状态数据，因为这个一个sqlexecute异步函数,所以状态数据是异步传递的。
		//把剩余的函数依次绑定给新创建的函数m1
		if( rff && rff.length>0 && m1 && m1.bind) rff.forEach(_f=>m1.bind(_f));// 绑定剩余,或者叫做绑定剩余的传递

		// 仅当m1中存在状态数据的的时候,对于sqlquery 这样的本身就是回调函数的接口，进行自我回调的。
		// 此处的自我回调用以保证m1中的函数f可以在非promise，这样的异步函数的自我回调的情况下,依旧可以持续触发下一次的徽标
		// 因为异步函数是CPS (Continuation passing style), 是参数 调用 函数的形式。这里  m1.ff[0](m1.stateData() 也是
		// 手动模拟这种机制。 CPS 就是把 参数调用函数，与一般的函数调用参数 刚好反过来
		const loop = m => { // 采用loop 模拟 for 循环，for  循环会导致对象的属性 赋值失败的情况。
			// 同步monad 的链式返回调的基础步伐：即 当且仅当m.stateData() 有效才给予进行CPS回调
			if(m && m.stateData && m.stateData() && m.ff[0] && isfunc(m.ff[0])){ // stateData 无标表明这是需要异步函数给予的徽标，不需要loop
				let _m = m.ff[0](m.stateData());// 用m.stateData来作为参数数据反调使用m.ff[0],因为此为同步函数，注意同步函数与异步函数在反调用的区别
				if(!_m) return; // 结果无效给予终止递归循环
				m.ff.slice(1).forEach(_f=>_m.bind(_f)); // 加载剩余的绑定函数
				loop(_m); // 递归调用
			}//if 同步monadCPS，返回掉
		};// loop

		// 继续参数回调非异步函数：标志就是stateDate 有效，异步含糊的stateData 无效。
		loop(m1); // 接连引爆的递归循环,同步函数的参数调用函数(m1:上的绑定集合函数)
	}//if(f) f 合法

}
module.exports.detonate = detonate;
